#ifndef CUDA_POISSON_RRT_KERNELS_H
#define CUDA_POISSON_RRT_KERNELS_H

#include <vector>
#include "cuda_rrt_types.h"

class GMesh;

namespace poisson
{
	namespace cuda
	{
		// initialize
		void initializeMemory(PoissonDiskSampleData& d_sampleData, GMesh** d_robot, GMesh** d_obstacle, RrtTree& d_startTree, RrtTree& d_goalTree);
		void uploadData(const std::vector<float>& vBoundsMin, const std::vector<float>& vBoundsMax, 
			const GMesh* h_robot, const GMesh* h_obstacle, const std::vector<SerializedStates>& vSerializedStates, 
			const std::vector<SerializedStates>& vTemplateStates, int numPrecomputedPDSamples,
			GMesh* d_robot, GMesh* d_obstacle, PoissonDiskSampleData& d_sampleData, PoissonDiskSampleData& d_templateData);
		void initializeRandomStates();

		// destroy
		void freeMemory(PoissonDiskSampleData& d_sampleData, PoissonDiskSampleData& d_templateData, 
			GMesh* d_robot, GMesh* d_obstacle, RrtTree& d_startTree, RrtTree& d_goalTree);

		// solve
		void setStartAndGoalStates(SerializedStates* h_startAndGoalStates, int numPrecomputedPDSamples, PoissonDiskSampleData& d_sampleData,
			int& numSamples, RrtTree& d_startTree, RrtTree& d_goalTree);

		void generateRandomStates(SerializedStates* generatedStates);

		void knn(SerializedStates* inRefStates, int inNumRefStates, SerializedStates* inQueryStates, int numQueryStates, unsigned int k, 
			float* internalDistance, float* outDistances, int* outIndices);

		void computeDistancesFromTreeNodesToStates(int inNumQueries, SerializedStates* inTreeNodeStates, int* inTreeNodeIndices, 
			SerializedStates* inDestStates, float* outDistR3, float* outDistSO3);

		void computeIntersectionStates(int inNumQueries, SerializedStates* inTreeNodeStates, int* inTreeNodeIndices, 
			int* intTreeNodeIndicesToSamples, int* inSampleLevels,
			SerializedStates* inDestStates, float* inDistR3, float* inDistSO3,
			SerializedStates* outItersectionStates);

		void computeDistancesToDestStates(int inNumQueries, int k, SerializedStates* inSampleStates, int* inSampleIndices, int* inSampleLevels,
			float* inDistancesFromIntersect, SerializedStates* indestStates, float* outDistances, int* inTreeNodeToSampleIndices, int* inTreeNodeIndices);

		void computeMinDistanceSamples(int inNumQueries, int k, SerializedStates* inSampleStates, int* inSampleIndices, 
			float* inDistances, int* outSampleIndices, float* outDistances);

		void generateNewSamplesIfNoNeighbor(int inNumQueries, PoissonDiskSampleData& d_sampleData, 
			float* inMinDistances, SerializedStates* inIntersectionStates, int* outSampleIndices,
			int* treeNodeIndices, int* inNodeIndices, SerializedStates* inDestStates);

		void checkCollision(int inNumQueries, SerializedStates* inTreeNodeStates, int* inTreeNodeIndices, 
			SerializedStates* inSampleStates, int* inSampleIndices,
			int* outResult, SerializedStates* outColFreeStates,
			GMesh* d_robot, GMesh* d_obstacle,
			float stateValidityCheckingResolutionR3, float stateValidityCheckingResolutionSO3);

		void removeDuplicatedPairs(int inNumQueries, int* inNodeIndices, int* inDestStateIndices, int* inResults);

		void addNewNodes(int inNumQueries, RrtTree& currentTree, RrtTree& otherTree, PoissonDiskSampleData& d_sampleData, 
			int* inNodeIndices, int* inDestStateIndices,
			int* inResults, SerializedStates* inColFreeStates, PoissonDiskSampleData& d_templateData);

		void downloadValues(PoissonDiskSampleData& d_sampleData, RrtTree& currentTree);

		void generateDestStatesForOtherTree(int inNumQueries, PoissonDiskSampleData& d_sampleData, 
			int* inDestStateIndices, int* inResults, SerializedStates* inColFreeStates, 
			SerializedStates* outStates);

		bool isSolutionFound(PoissonDiskSampleData& d_sampleData, RrtTree& startTree, RrtTree& goalTree, 
			std::vector<SerializedStates>& solution);

		void reset();
	}
}

namespace rrtconnect
{
	namespace cuda
	{
		void setStartAndGoalStates(SerializedStates* h_startAndGoalStates, PoissonDiskSampleData& d_sampleData,
			RrtTree& d_startTree, RrtTree& d_goalTree);

		void uploadData(const std::vector<float>& vBoundsMin, const std::vector<float>& vBoundsMax, 
			const GMesh* h_robot, const GMesh* h_obstacle, 
			GMesh* d_robot, GMesh* d_obstacle, PoissonDiskSampleData& d_sampleData);

		void freeMemory(PoissonDiskSampleData& d_sampleData, 
			GMesh* d_robot, GMesh* d_obstacle, RrtTree& d_startTree, RrtTree& d_goalTree);

		void setDestStates(int inNumQueries, SerializedStates* inTreeNodeStates, int* inNodeIndices, SerializedStates* inoutDestStates, 
			int* outConnectionCheck, bool isCurrentTree, float maxDistance);

		void checkCollision(int inNumQueries, SerializedStates* inTreeNodeStates, int* inTreeNodeIndices, 
			SerializedStates* inDestStates,
			int* outResult, SerializedStates* outColFreeStates,
			GMesh* d_robot, GMesh* d_obstacle,
			float stateValidityCheckingResolutionR3, float stateValidityCheckingResolutionSO3, int* inoutConnectionCheck);

		void addNewNodes(int inNumQueries, RrtTree& currentTree, RrtTree& otherTree, 
			int* inNodeIndices, SerializedStates* inDestStates,
			int* inResults, SerializedStates* inColFreeStates);

		void downloadValues(RrtTree& currentTree);

		void updateDestStates(int inNumQueries, SerializedStates* inoutDestStates, int* inResults, SerializedStates* inColFreeStates);

		bool isSolutionFound(int inNumQueries, RrtTree& currentTree, RrtTree& otherTree, std::vector<SerializedStates>& solution,
			int* inConnectionCheck, int* inCurrentTreeNodeIndices, int* inOtherTreeNodeIndices, SerializedStates* inDestStates);
	}
}

#endif